#include <stdio.h>
#include <stdlib.h>
#include "processo.h"
#include "in_out.h"


apontador *alocar_tabela_processos (int n){
	/* ponteiro para o vetor */
	apontador *v;
	
	if (n < 1) { /* verifica parametros recebidos */
		printf ("** Erro: Parametro invalido **\n");
		return (NULL);
	}
	/* aloca o vetor */
	v = (apontador *) calloc (n, sizeof(apontador));
	
	if (v == NULL) {
		printf ("** Erro: Memoria Insuficiente **");
		return (NULL);
	}
	return (v);
	/* retorna o ponteiro para o vetor */
}

instrucoes *alocar_vetor (int n){
	/* ponteiro para o vetor */
	instrucoes *v;
	int x;
	if (n < 1) { /* verifica parametros recebidos */
		printf ("** Erro: Parametro invalido **\n");
		return (NULL);
	}
	/* aloca o vetor */
	v = (instrucoes *) calloc (n, sizeof(instrucoes));
	
	for(x = 0; x< n; x++){
		v[x].comando = (char *) malloc(20 * sizeof(char));
		v[x].instrucao = (char *) malloc(20 * sizeof(char));
	}
	if (v == NULL) {
		printf ("** Erro: Memoria Insuficiente **");
		return (NULL);
	}
	return (v);
	/* retorna o ponteiro para o vetor */
}

instrucoes *liberar_vetor (instrucoes *v){
	
	if (v == NULL) return (NULL);
	free(v);
	/* libera o vetor */
	return (NULL); /* retorna o ponteiro */
}

void adiciona_processo_pcb(processo_simulado *p){
	
	/*Adiciona o novo processo à tabela pcb*/
	tabela_processos[qtd_processos_criados] = p;
	qtd_processos_criados++;
}

/*
  tipo_ordenacao, vai ser definido pelo tipo de escalonador a ser usado.
  Ordenaçao pode ser feita em: 1-> ordem decrescente de prioridade. 2-> em ordem crescente
  da relacao tamanho - contador, significando o processo que vai demorar menos tempo para
  acabar.
*/
void adiciona_processo_lista_prontos(processo_simulado *p){
	
	/*Insere no final da fila*/
	processos_prontos[qtd_processos_prontos] = p;
	qtd_processos_prontos++;
	
	/*Faz a ordenação, de acordo com o tipo de escalonamento escolhido*/
	switch(tipo_escalonador){
		
		/*Escalonador de prioridade.*/
		case 1:
		
			break;
		/*Escalonador de menor tempo. */
		case 2:
			break;	
	}
}

/*
  tipo_ordenacao, vai ser definido pelo tipo de escalonador a ser usado.
  Ordenaçao pode ser feita em: 1-> ordem decrescente de prioridade. 2-> em ordem crescente
  da relacao tamanho - contador, significando o processo que vai demorar menos tempo para
  acabar. 0 -> Sem ordenaçao.
*/
void adiciona_processo_lista_bloqueados(processo_simulado *p){
	processos_bloqueados[qtd_processos_bloqueados] = p;
	qtd_processos_bloqueados++;
}

/**/
void remover_tabela_pcb(int id){
	int x;
	for(x = id; x < qtd_processos_criados-1; x++){
		tabela_processos[x] = tabela_processos[x+1];
	}
	qtd_processos_criados--;
}

/*Essa funçao pode muito bem ser otimizada.*/
void remover_processo(int id, int tipo_lista){
	
	int x;
	switch(tipo_lista){
		/*Remover processo bloqueado*/
		case 1:
			for(x =0; x<qtd_processos_bloqueados; x++){
				/*Procura pelo indice que o processo ocupa na lista*/
				if(processos_bloqueados[x]->id == id){
					int c;
					/*Subescreve a posição ocupada e shifta o resto da lista*/
					for(c = x; c < qtd_processos_bloqueados -1; c++){
						processos_bloqueados[c] = processos_bloqueados[c+1];
					}
					/*Decrementa a quantidade de processos bloqueados*/
					qtd_processos_bloqueados--;	
					break;
				}
			}
			break;
		/*Remover processo pronto*/
		case 2:
			
			for(x =0; x<qtd_processos_prontos; x++){
				/*Procura pelo indice que o processo ocupa na lista*/
				if(processos_prontos[x]->id == id){
					int c;
					/*Subescreve a posição ocupada e shifta o resto da lista*/
					for(c = x; c < qtd_processos_prontos -1; c++){
						processos_prontos[c] = processos_prontos[c+1];
					}
					/*Decrementa a quantidade de processos prontos*/
					qtd_processos_prontos--;	
					break;
				}
			}
			break;
	}
}

void imprime_lista_processos(apontador *p,int tipo){
	apontador *v;
	int contador, x;
	v = p;
	switch(tipo){
		case 1:
			contador = qtd_processos_criados;
			break;
		case 2:
			contador = qtd_processos_prontos;
			break;
		case 3:
			contador = qtd_processos_bloqueados;
			break;		
	}
	
	/*Verifica se a lista está vazia*/
	if (contador == 0){ printf("Não existem processos nessa lista!\n"); return;}
	
	/*Imprime os ids */
	for(x=0;x<1;x++){
		printf("id -> %d\n",v[x]->id);
	}
}

/*--------------------------------------------------------------------------*/
/* Função responsável por criar a lista simplesmente encadeada.
   Possui célula cabeça.                                                    */
/*--------------------------------------------------------------------------*/

void criarLista(Lista *L1){
	
	// L1->primeiro é a célula cabeça
	L1->primeiro = (Apontador) malloc(sizeof(celula));
	L1->ultimo = L1->primeiro;
	L1->primeiro->prox = NULL;
	
}


/*--------------------------------------------------------------------------*/
/* Função responsável por desalocar a lista.                                 */
/*--------------------------------------------------------------------------*/

void desalocarLista (Lista *L1) {

	Apontador aux1 = L1->primeiro;
	Apontador aux2 = NULL;
	
	while (aux1 == L1->ultimo) {

		aux2 = aux1;
		aux1 = aux1->prox;
		free(aux2);
	}
	
	free(aux1);
	//free(L1->primeiro);
	
}

/*--------------------------------------------------------------------------*/
/*Função responsável por imprimir a lista na tela.  */
/*--------------------------------------------------------------------------*/

void mostrarLista (Lista *L1) {  
	
	if(!Vazia(L1)){
		// L1->primeiro é a célula cabeça
		Apontador aux = L1->primeiro->prox;
			
		while (aux != NULL){
			printf("%d:   %d:   %d:   %d:   %d:\n", aux->processo->id,aux->processo->pid,aux->processo->prioridade,aux->processo->tempo_usado,aux->processo->contador_programa);
			aux = aux->prox;
		}
	}printf("Lista Vazia!\n");
}

/*--------------------------------------------------------------------------*/
/* Função responsável por inserir um elemento na lista.                       */
/*--------------------------------------------------------------------------*/

void inserirElemento(processo_simulado *p, Lista *L1){

	L1->ultimo->prox = (Apontador) malloc(sizeof(celula));
	L1->ultimo = L1->ultimo->prox;
	L1->ultimo->processo = p;						
	L1->ultimo->prox = NULL;
	L1->numero_processos++;
	
}


/*--------------------------------------------------------------------------*/
/* Função que busca um elemento na lista e retorna sua posição na lista.    */
/*--------------------------------------------------------------------------*/
Apontador buscarElemento(int x, Lista *L1){
	
	Apontador aux = L1->primeiro->prox;
	
	while((aux != NULL) && (aux->processo->id != x)){
		
		aux = aux->prox;		
		
	}
	
	// Retorna NULL ou o apontador para a célula com o item buscado
	return aux;
}

/*--------------------------------------------------------------------------*/
/* Função que verifica se a lista está vazia ou não. 					   */
/*--------------------------------------------------------------------------*/
int Vazia(Lista *L1) {
	
	return (L1->primeiro == L1->ultimo);
}

/*--------------------------------------------------------------------------*/
/* Função que elemento da lista. 					   */
/*--------------------------------------------------------------------------*/
void remover(Lista *L1, int id) {
	if(!Vazia(L1)){
		Apontador aux1 = L1->primeiro;
		Apontador aux2 = L1->primeiro->prox;
		
		while((aux2 != NULL) && (aux2->processo->id != id)){
			aux1 = aux1->prox;
			aux2 = aux2->prox;		
		}
		
		if (aux2 != NULL){
			aux1->prox = aux2->prox;
			free(aux2);
			L1->numero_processos--;
		}else{
			printf("\nItem não presente na lista!\n");
		}
	}else{
		printf("Lista vazia!\n");
	}	
}
